package com.esdk.utils;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.esdk.esdk;
import com.esdk.interfaces.FuncNoArg;
import lombok.NonNull;
import org.apache.poi.ss.formula.functions.T;

import java.net.URLEncoder;
import java.time.LocalDate;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public class EasyMap{

  public static Map createMap(Object... args) {
  	LinkedHashMap result=new LinkedHashMap();
  	for(int i=0,n=args.length;i<n;i+=2) {
  		result.put(args[i],args[i+1]);
  	}
  	return result;
  }

	/** 基于fastjson将map转成bean对象 */
	public static <T> T toBean(Map<String, Object> map, @NonNull TypeReference<T> typeRef) {
		if(map == null) return null;
		return new JSONObject(map).toJavaObject(typeRef);
	}

	/**
	 * 创建map，支持键值插入、map合并，确保key为String类型
	 * <code>
	 *	EasyMap.mapFrom(
	 *	"key1", new Object(), // 键值对
	 *	Map.of("aaa", 1, "bbb", 2), // map合并
	 *	"key2", new HashMap() // 键值对
	 *	)
	 * <code/>
	 */
	public static Map<String, Object> mapFrom(Object ...args){
		LinkedHashMap<String, Object> result=new LinkedHashMap();
		String key = null;
		for(int i = 0; i < args.length; ++i){
			if(key == null){
				if(args[i] instanceof Map map)
					result.putAll(map); // map则合并
				else if(args[i] instanceof String str)
					key = str; // 字符串作为键
				else
					throw new NullPointerException("找不到键");
			}else{
				result.put(key,args[i]); // 前面存在键，则直接作为当前值的键
				key = null;
			}
		}
		return result;
	}

	public static Map createMap(String mapStr) {
		return valueOf(mapStr);
	}

  /**自动转换值的类型*/
  public static Map<String,Object> valueOf(String mapStr){
  	return strToMap(mapStr,",",true);
  }

  /**值为字符串，不会自动转换为其他类型*/
  public static Map<String,String> strToMap(String s){
  	return strToMap(s,",",false);
  }

  /**字符串转换为Map*/
  public static Map strToMap(String mapStr,String delimter,boolean isConvertDataType){
  	if(mapStr==null||mapStr.length()==0)
  		return new LinkedHashMap();
  	else if(mapStr.charAt(0)=='{'&&mapStr.charAt(mapStr.length()-1)=='}') {
  		mapStr=mapStr.substring(1,mapStr.length()-1);
  	}
		LinkedHashMap result=new LinkedHashMap<String,String>(8);
		for (StringTokenizer st=new StringTokenizer(mapStr,delimter);st.hasMoreElements();) {
			String temp[]=st.nextToken().split("[=:]",2);
			if(temp.length==2){
				result.put(temp[0].trim(),isConvertDataType?temp[1]:convert(temp[1]));
			}
		}
		return result;
  }


  private static Object convert(String value){
	  if(value==null)
		  return null;
	  else if(value.equals("null"))
		  return null;
	  else if(value.equals("true"))
		  return Boolean.TRUE;
	  else if(value.equals("false"))
		  return Boolean.FALSE;
	  else if(value.matches("\\w*"))
		  return value;
	  else if(EasyMath.isNumeric(value))
		  return EasyMath.toNumber(value);
	  else if(EasyTime.isDate(value)){
		  return EasyTime.valueOf(value);
	  }
	  else
		  return value;
  }

	public static Map<String,String> urlParamsToMap(String httpParams){
		LinkedHashMap result=new LinkedHashMap();
		if(httpParams!=null) {
			for(StringTokenizer st=new StringTokenizer(httpParams,"&");st.hasMoreElements();) {
				String v=(String)st.nextToken();
				String[] p=splitFirst(v,"=");
				result.put(p[0],p[1]);
			}
		}
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		return super.equals(obj);
	}

	public static <T extends Map<String,Object>> T trim(@NonNull T map) {
		for(Iterator<Entry<String,Object>> iter=map.entrySet().iterator();iter.hasNext();) {
			Entry entry=iter.next();
			if(entry.getValue()==null){}
			else if(entry.getValue() instanceof CharSequence)
				entry.setValue(esdk.str.trim(entry.getValue().toString()));
			else if(entry.getValue() instanceof Map)
				trim((Map)entry.getValue());
		}
		return map;
	}

	public static String toHttpParams(Map<String,String> params){
		return toHttpParams(params,false);
	}

	public static String toHttpParams(Map<String,String> params,boolean isEncode){
		StringBuilder result=new StringBuilder();
		if(params!=null) {
			try{
				for(Entry entry:params.entrySet()) {
					if(isEncode)
						result.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(),"utf8")).append("&");
					else
						result.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
				}
			}catch(Exception e){
				throw new RuntimeException(e);
			}
		}
		if(result.length()>0)
			result.deleteCharAt(result.length()-1);
		return result.toString();
	}

	public static String[] splitFirst(@NonNull String str,@NonNull String delimter) {
		int pos=str.indexOf(delimter);
		String[] result=new String[] {null,null};
		if(pos>=0) {
			result[0]=str.substring(0,pos);
			result[1]=str.substring(pos+1);
		}
		else {
			result[0]=str;
			result[1]="";
		}
		return result;
	}

	public static Map<String,?> toUnderlineCaseMap(@NonNull Map<String,?> map) {
		return toUnderlineCaseMap(map,true);
	}

	public static Map<String,?> toUnderlineCaseMap(Map<String,?> map,boolean isLowerCase) {
		try{
			Map result=map;
			String[] keys=map.keySet().toArray(new String[0]);
			for(String key:keys){
				Object value=map.remove(key);
				key=esdk.str.toUnderlineCase(key);
				if(isLowerCase)
					key=key.toLowerCase();
				result.put(key,value);
			}
			return result;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	/**把key改为驼峰格式*/
	public static Map toCamelCaseMap(@NonNull Map map){
		try{
			Map result=esdk.reflect.safeNewInstance(map.getClass());
			for(Iterator iter=map.entrySet().iterator();iter.hasNext();){
				Entry entry=(Entry)iter.next();
				String key=(String)entry.getKey();
				result.put(esdk.str.toCamelCase(key),entry.getValue());
			}
			return result;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}

	public static Map clearNull(Map map) {
		Object[] keys=map.keySet().toArray();
		for(int i=0;i<keys.length;i++){
			Object key=keys[i];
			if(map.get(key)==null) {
				map.remove(key);
			}
		}
		return map;
	}

	/**如果value值为空，则返回key值*/
	public static <K,V>V getValue(@NonNull Map<K,V> map,@NonNull K valOrKey) {
		return getValue(map,valOrKey,(V)valOrKey);
	}

	/**从多个map中取值，获取不了就返回key值*/
	public static String getValue(@NonNull Map<String,String>[] maps,@NonNull String key){
		String result=null;
		for(int i=0;i<maps.length;i++){
			result=maps[i].get(key);
			if(result!=null)
				break;
		}
		if(result==null)
			result=key;
		return result;
	}

	/**
	 * 获取或创建value
	 * @param valueCls 如果value为空，则通过反射创建新实例，并put到map中
	 * */
	public static <K,V> @NonNull V getValue(@NonNull Map<K,V> map,@NonNull K key,@NonNull Class<V> valueCls,Object... constructorParams){
		V val=map.get(key);
		if(val==null){
			val=(V)esdk.reflect.safeNewInstance(valueCls,constructorParams);
			map.put(key,val);
		}
		return val;
	}

	/**
	 * 通过key获取value，如果没有则回调获取实例方法，避免每次自动创建实例
	 * */
  public static <K,V> @NonNull V getVal(@NonNull Map<K,V> map,@NonNull K key,FuncNoArg<V> getValueFunc){
		V val=map.get(key);
		if(val==null){
      try{
        val=getValueFunc.invoke();
				map.put(key,val);
      }catch(Exception e){
        throw esdk.tool.wrapThrowble(e);
      }
		}
		return val;
	}


	/**
	 * 获取或创建value
	 * @param defVal 如果value为空，则把默认值put到map中，并返回默认值
	 * */
	public static <K,V> V getValue(@NonNull Map<K,V> map,@NonNull K key,@NonNull V defVal){
		V val=map.get(key);
		if(val==null){
			val=defVal;
			map.put(key,defVal);
		}
		return val;
	}
	/**忽略大小写*/
	public static boolean containsKey(@NonNull Map map,Object key){
		boolean result=map.containsKey(key);
		if(!result&&(key instanceof CharSequence)){
			for(Iterator iter=map.keySet().iterator();iter.hasNext();){
				String realKey=iter.next().toString();
				if(EasyStr.equals(realKey,key.toString(),true))
					return map.containsKey(realKey);
			}
		}
		return result;
	}

	public static String mapToStr(Map<String, ?> map, char express) {
		return mapToStr(map,express,true);
	}

	public static String mapToStr(Map<String, ?> map, char express,boolean includeBrace) {
		return mapToStr(map,express,",",includeBrace);
	}

	/**
	 * @param includeBrace 是否首尾输出大括号
	 * */
	public static String mapToStr(Map<String, ?> map, char express,String splitter,boolean includeBrace) {
			StringBuffer result = new StringBuffer();
			if(includeBrace)
				result.append('{');
			if(map!=null){
				for(Iterator iter=map.entrySet().iterator();iter.hasNext();){
					Entry e=(Entry)iter.next();
					result.append(e.getKey()).append(express).append(e.getValue());
					result.append(iter.hasNext()?splitter:"");
				}
			}
			if(includeBrace)
				result.append('}');
			return result.toString();
	}

	/**
	 * @param includeBrace 是否首尾输出大括号
	 * */
	public static String mapToStr(@NonNull Map map,boolean includeBrace) {
		return mapToStr(map, '=',includeBrace);
	}

	public static String mapToStr(Map map) {
			return mapToStr(map, '=',true);
	}

	/**
	 * 把多个map合并，注意修改和返回的是第一个map，而不是创建一个新的map实例
	 * @param resultMap 这个map实例会被修改，如果不想被删除的话，可以new HashMap()
	 * @param maps 被合并的key和value，不会被修改。
	 * */
	public static <M extends Map> M merge(M resultMap,M... maps){
		return merge(true,resultMap,maps);
	}

	/**
	 * 把多个map合并，注意修改和返回的是第一个map，而不是创建一个新的map实例
	 * @param resultMap 这个map实例会被修改，如果不想被删除的话，可以new HashMap()
	 * @param maps 被合并的key和value，不会被修改。
	 * */
	public static <M extends Map> M merge(boolean isOverwrite,@NonNull M resultMap,M... maps){
		for(Map map:maps) {
			for(Object key:map.keySet()) {
				if(isOverwrite || !resultMap.containsKey(key) || esdk.obj.isEmpty(resultMap.get(key)))
					resultMap.put(key,map.get(key));
			}
		}
		return resultMap;
	}

	/**
	 * 根据给定的键集合将map分成两个map
	 * @param map 源map
	 * @param keys 键集合
	 * @return 给定键集合所对应的map
	 */
	public static <K, V> Map<K, V> splitKeys(Map<K, V> map, Set<K> keys){
		Map<K, V> newMap = esdk.reflect.safeNewInstance(map.getClass());
		V value;
		for (K key : keys) {
			value = map.remove(key);
			if(value != null)
				newMap.put(key, value);
		}
		return newMap;
	}

	public static void main(String[] args){
		String httpParams="NAME=买家1&USERTYPE=0&RTURL=http://128.128.85.133:8080/ccb-epay/epay/execReceipt.do?txType=login_rtn&TRANSID=120424175253546920&TXCODE=EP0013&MERCHANTID=0001";
		esdk.tool.assertEquals(EasyMap.urlParamsToMap(httpParams).toString(),"{NAME=买家1, USERTYPE=0, RTURL=http://128.128.85.133:8080/ccb-epay/epay/execReceipt.do?txType=login_rtn, TRANSID=120424175253546920, TXCODE=EP0013, MERCHANTID=0001}");
		esdk.tool.assertEquals(strToMap(null).toString(),"{}");
		esdk.tool.assertEquals(mapToStr(null),"{}");
		esdk.tool.assertEquals(mapToStr(strToMap("{}")),"{}");
		esdk.tool.assertEquals(strToMap("{name=李元富, sex=男}").toString(),"{name=李元富, sex=男}");
		esdk.tool.assertEquals(strToMap("{name:李元富, sex:男}").toString(),"{name=李元富, sex=男}");
		esdk.tool.assertEquals(createMap("name","张三","code","zhang","sex","男","age",18).toString(),"{name=张三, code=zhang, sex=男, age=18}");
		esdk.tool.assertEquals(createMap("name","李远行","age",18,"valid",true,"createTime",LocalDate.parse("2008-12-27")).toString(),"{name=李远行, age=18, valid=true, createTime=2008-12-27}");
		esdk.tool.assertEquals(mapToStr(strToMap("{name=李元富,sex=男}")), "{name=李元富,sex=男}");
		esdk.tool.assertEquals(mapToStr(strToMap("{name=李元富,sex}")), "{name=李元富}");
		esdk.tool.assertEquals(mapToStr(valueOf("{name:李元富,sex:男,isMale:true,age:40}"),':'), "{name:李元富,sex:男,isMale:true,age:40}");
		esdk.tool.assertEquals(merge(false,valueOf("{name:李元富,isMale:true,age:40}"),valueOf("{sex:男,age:50}")).toString(),"{name=李元富, isMale=true, age=40, sex=男}");
		esdk.tool.assertEquals(merge(true,valueOf("{name:李元富,age:40}"),valueOf("{name:李元富,sex:男,age:50}")).toString(),"{name=李元富, age=50, sex=男}");
		esdk.tool.printAssertInfo();
	}

}
